package com.ls.mybatis.core;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;

import java.util.*;

public class SqlSession {
    private  ConnectionPool connectionPool;
    private Map<String, MapperInfo> mapperInfos;
    private Collections Connections;

    public SqlSession(ConnectionPool connectionPool, Map<String, MapperInfo> mapperInfos){
        this.connectionPool = connectionPool;
        this.mapperInfos = mapperInfos;
    }
    /**
     * 执行更新（DML语句）的方法
     */
    public int update(String sqlId, Object obj){
        if(mapperInfos == null || mapperInfos.isEmpty()){
            throw new RuntimeException("没有读取到映射文件信息");
        }

        MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId,null);
        if(mapperInfo == null){
            throw new RuntimeException("我找不到要执行的sql" + sqlId + "......");
        }
        int result = -1;
        Connection con = null;

        try {
            con = connectionPool.getConnection();
            //获取sql语句
            String sql = mapperInfo.getSql();
            //获取执行语句中占位符的名称
            List<String> params = mapperInfo.getParams();
            List<Object> paramsValue = null;

            if(params != null && !params.isEmpty()){
                System.out.println("映射文件中输入的参数值类型是： " + mapperInfo.getParameterType());
                //根据占位符对应的名称从指定的对象中获取参数值
                paramsValue = getParamsValue(mapperInfo.getParameterType(), params, obj);
            }
             System.out.println("执行的sql语句是：" + sql);
             System.out.println("参数列表的值为：" + paramsValue);
             
            /**
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 重要的事情说三遍
             */
            DBHelper dbHelper = new DBHelper();
            result = dbHelper.update(con, sql, paramsValue);

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            if(con != null){
                this.close(con);
            }
        }
        return result;
    }

    /**
     * 返回一个对象的方法
     * @param sqlId
     * @param obj
     * @param <T>
     * @return
     */
    public <T> T selectOne(String sqlId, Object obj){
        if(mapperInfos == null || mapperInfos.isEmpty()){
            throw new RuntimeException("没有读取到映射文件信息");
        }
        //根据sqlid获取对应的sql映射语句对象
        MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId,null);

        if(mapperInfo == null){
            throw new RuntimeException("我找不到要执行的sql" + sqlId + "......");
        }

        Connection con = null;
        try {
            con = connectionPool.getConnection();
            //获取sql语句
            String sql = mapperInfo.getSql();
            //获取执行语句中占位符的名称
            List<String> params = mapperInfo.getParams();
            List<Object> paramsValue = null;

            if(params != null && !params.isEmpty()){
                System.out.println("映射文件中输入的参数值类型是：" + mapperInfo.getParameterType());
                //根据占位符对应的名称从指定的对象中获取参数值
                paramsValue = getParamsValue(mapperInfo.getParameterType(), params, obj);
            }
            System.out.println("执行的sql语句是：" + sql);
            System.out.println("参数列表的值为：" + paramsValue);

            /**
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 重要的事情说三遍
             */
            DBHelper dbHelper = new DBHelper();
            //获取返回值类型
            String resultType = mapperInfo.getResultType();
            //int 数据类型
            if("int".equalsIgnoreCase(resultType)){
                return (T) dbHelper.findSingle(con, sql, paramsValue);
            }
            //如果是map
            if("map".equalsIgnoreCase(resultType)){
                return (T) dbHelper.findSingle(con, sql, paramsValue);
            }
            //如果是对象
            Class<?> c = Class.forName(resultType);
            return dbHelper.findSingle(con, c , sql, paramsValue);

        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            if(con != null){
                this.close(con);
            }
        }
        return null;
    }

    /**
     * 带参数的返回集合对象的查询
     * @param sqlId
     * @param obj
     * @param <T>
     * @return
     */
    public <T> List<T> selectList(String sqlId, Object obj){
        if(mapperInfos == null || mapperInfos.isEmpty()){
            throw new RuntimeException("没有读取到映射文件信息");
        }
        //根据sqlid获取对应的sql映射语句对象
        MapperInfo mapperInfo = mapperInfos.getOrDefault(sqlId,null);

        if(mapperInfo == null){
            throw new RuntimeException("我找不到要执行的sql" + sqlId + "......");
        }

        Connection con = null;
        try {
            con = connectionPool.getConnection();
            //获取sql语句
            String sql = mapperInfo.getSql();
            //获取执行语句中占位符的名称
            List<String> params = mapperInfo.getParams();
            List<Object> paramsValue = null;

            if(params != null && !params.isEmpty()){
                System.out.println("映射文件中输入的参数值类型是： " + mapperInfo.getParameterType());
                //根据占位符对应的名称从指定的对象中获取参数值
                paramsValue = getParamsValue(mapperInfo.getParameterType(), params, obj);
            }
            System.out.println("执行的sql语句是：" + sql);
            System.out.println("参数列表的值为：" + paramsValue);

            /**
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 最关键一步执行数据库语句
             * 重要的事情说三遍
             */
            DBHelper dbHelper = new DBHelper();
            //获取返回值类型
            String resultType = mapperInfo.getResultType();
            if("map".equalsIgnoreCase(resultType)){
                return (List<T>) dbHelper.findMutile(con, sql, paramsValue);
            }

            Class<?> c = Class.forName(resultType);
            return dbHelper.findMutile(con, c , sql, paramsValue);
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
            if(con != null){
                this.close(con);
            }
        }
        return null;
    }


    /**
     * 不带参数的返回集合对象的查询
     * @param sqlId
     * @param <T>
     * @return
     */
    public <T> List<T> selectList(String sqlId){
        return this.selectList(sqlId, null);
    }


    /**
     * 关闭连接
     * 将获取到的连接还给数据库连接池
     * @param con
     */
    public void close(Connection con){
        connectionPool.freeConnection(con);
    }


    /**
     * 根据传入都对象的参数名称
     * 和对应的get方法获取该对象的值
     * @param parameterType
     * @param paramsName
     * @param obj
     * @return
     */
    private List<Object> getParamsValue(String parameterType, List<String> paramsName, Object obj) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
        //保存对象参数的容器
        List<Object> params = new ArrayList<Object>();

        //判空操作
        if(paramsName == null || paramsName.isEmpty() || StringUtil.checkNull(parameterType) || obj == null){
            return Connections.emptyList();
        }

        //判断是不是基础数据类型
        if("int".equalsIgnoreCase(parameterType) || "float".equalsIgnoreCase(parameterType) || "double".equalsIgnoreCase(parameterType)
            || "string".equalsIgnoreCase(parameterType) || "Integer".equalsIgnoreCase(parameterType)){
            params.add(obj);
            return params;
        }

        //判断是不是map对象
        if("map".equalsIgnoreCase(parameterType)){
            Map<String, Object> map =(Map<String, Object>) obj;
            for(String name : paramsName){
                params.add(map.getOrDefault(name, ""));
            }
            return params;
        }

        //是对象类型，先加载这个类
        Class<?> clazz = Class.forName(parameterType);
        //通过反射获取这个类中的方法
        Method[] methods = clazz.getDeclaredMethods();
        if(methods == null || methods.length <= 0){
            return params;
        }
        //获取这个类中所有的getter方法
        Map<String, Method> getters = new HashMap<String, Method>();
        //用于保存函数名字的临时变量
        String methodName = null;
        for(Method method : methods){
            methodName = method.getName();
            //获取这个类中的get方法
            if(methodName.startsWith("get")){
                //将方法名字和方法对象存入map中
                getters.put(methodName, method);
            }
        }

        Method method = null;
        for(String name : paramsName){
            //将属性名第一个字母大写
            methodName = "get" + name.substring(0,1).toUpperCase() + name.substring(1);
            //获取对应的方法
            method = getters.get(methodName);
            //没有对应方法存入空
            if(method == null){
                params.add(null);
                continue;
            }
            //根据方法名字反向激活这个方法，并将其获取的值存入参数容器中
            params.add(method.invoke(obj));
        }
        return params;
    }

}
